package model.algorithms.embedding;

import ilog.concert.IloException;
import ilog.concert.IloLinearNumExpr;
import ilog.concert.IloNumExpr;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;
import model.components.networks.network.VirtualNetwork;
import model.components.networks.network.PhysicalNetwork;

public class ViNE extends EmbeddingAlgorithm
{
	public ViNE( String name )
	{
		super( name );
	}
	
	@Override
	protected void minimize( IloCplex cplex, IloNumVar f[][][], IloNumVar x[][][], VirtualNetwork virtualNetwork, PhysicalNetwork physicalNetwork, PhysicalNetwork augmentedPhysicalNetwork ) throws IloException 
	{
		IloLinearNumExpr linksCost = this.linksCost( cplex, f, virtualNetwork, augmentedPhysicalNetwork );
		IloLinearNumExpr nodesCost = this.nodesCost( cplex, x, virtualNetwork, physicalNetwork );
		//IloNumExpr sumCosts = this.sumCosts( cplex, costsLinks, costsNodes );
			
		cplex.addMinimize( this.sumCosts( cplex, linksCost, nodesCost ) );
	}
	private IloLinearNumExpr linksCost( IloCplex cplex, IloNumVar f[][][], VirtualNetwork virtualNetwork, PhysicalNetwork augmentedPhysicalNetwork ) throws IloException
	{
		IloLinearNumExpr linksCost = cplex.linearNumExpr();
		
		double availableBandwidths[][] = augmentedPhysicalNetwork.getPhysicalLinksAvailableBandwidths();
		
		for( int u = virtualNetwork.getNodes().size(); u < augmentedPhysicalNetwork.getNodes().size(); u++ )
			for( int v = virtualNetwork.getNodes().size(); v < augmentedPhysicalNetwork.getNodes().size(); v++ )
			{
				// 1 <= alpha <= bwAvailable(u,v). Load balancing. 
				double alpha = 1.0;
				double delta = Double.MIN_VALUE;
				double linkCost = alpha / ( availableBandwidths[ u ][ v ] + delta );
					
				for( int i = 0; i < virtualNetwork.getVirtualLinks().size(); i++ )
					linksCost.addTerm( linkCost, f[ i ][ u ][ v ] );	// (5.1)
			}
		
		return linksCost;
	}
	private IloLinearNumExpr nodesCost( IloCplex cplex, IloNumVar x[][][], VirtualNetwork virtualNetwork, PhysicalNetwork physicalNetwork ) throws IloException
	{
		IloLinearNumExpr nodesCost = cplex.linearNumExpr();
		
		double virtualNodesCapacities[][]    = virtualNetwork.getNodesCapacities();
		double availablePhysicalNodesCapacities[][] = physicalNetwork.getPhysicalNodesAvailableCapacities();
		
		for( int j = 0; j < 3; j++ )
			for( int a = 0; a < nodesNumberByType( virtualNetwork ).size(); a++ )
				for( int w: this.nodesNumberByType( physicalNetwork ).get( a ) )
				{
					// 1 <= beta <= capacAvailable(w). Load balancing. 
					double beta = 1;
					double delta = Double.MIN_VALUE;
					double nodeCost = beta / ( availablePhysicalNodesCapacities[ j ][ w ] + delta );
					
					for( int m: this.nodesNumberByType( virtualNetwork ).get( a ) )
						for( int i = 0; i < virtualNetwork.getVirtualLinks().size(); i++ )
							nodesCost.addTerm( nodeCost * virtualNodesCapacities[ j ][ m ], x[ i ][ m ][ w + virtualNetwork.getNodes().size() ] );	// (5.2)
				}
		
		return nodesCost;
	}
	private IloNumExpr sumCosts( IloCplex cplex, IloLinearNumExpr linksCost, IloLinearNumExpr nodesCost ) throws IloException
	{
		return cplex.sum( linksCost, nodesCost );
	}

}
